/**
 * 
 */
package com.grupit.module.user.web.gwt.client.pluggablefeatures.activity;

import java.util.List;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.user.client.ui.AcceptsOneWidget;
import com.google.web.bindery.requestfactory.shared.EntityProxyId;
import com.google.web.bindery.requestfactory.shared.Request;
import com.grupit.engine.util.FormatterLogger;
import com.grupit.module.user.web.gwt.client.common.util.RequestQueue;
import com.grupit.module.user.web.gwt.client.control.activity.PrefetchActivity;
import com.grupit.module.user.web.gwt.client.control.activity.UsesLoginInfoActivity;
import com.grupit.module.user.web.gwt.client.control.event.LoggedUserChangedEvent;
import com.grupit.module.user.web.gwt.client.control.event.RefreshNowEvent;
import com.grupit.module.user.web.gwt.client.control.ioc.GrupitClientFactory;
import com.grupit.module.user.web.gwt.client.control.ioc.GrupitClientFactory.GlobalProvider;
import com.grupit.module.user.web.gwt.client.control.ioc.GrupitClientFactory.GlobalProvider.GlobalReceiver;
import com.grupit.module.user.web.gwt.client.control.ioc.GrupitExceptionController;
import com.grupit.module.user.web.gwt.client.control.ioc.GrupitMessageBoxFactory;
import com.grupit.module.user.web.gwt.client.control.ioc.GrupitRequestTransport;
import com.grupit.module.user.web.gwt.client.integration.database.domain.UserVo;
import com.grupit.module.user.web.gwt.client.pluggablefeatures.ioc.NoteBoardView;
import com.grupit.module.user.web.gwt.client.pluggablefeatures.ui.NoteBoardViewImpl;
import com.grupit.module.user.web.gwt.shared.common.util.GrupitReceiverAdapter;
import com.grupit.module.user.web.gwt.shared.pluggablefeatures.proxy.NoteProxy;
import com.grupit.module.user.web.gwt.shared.pluggablefeatures.request.NoteRequest;
import com.grupit.module.user.web.gwt.shared.pluggablefeatures.request.NoteRequestFactory;

import es.chaveweb.poZit.client.mvp.widget.PoZitBoardPanel;
import es.chaveweb.poZit.client.mvp.widget.PoZitBoardPanel.Presenter.ConfirmResponse;
import es.chaveweb.poZit.client.mvp.widget.PoZitData;
import es.chaveweb.poZit.shared.proxy.PoZitProxy;

/**
 * @author jlp
 * 
 */
public class NoteBoardActivity extends UsesLoginInfoActivity implements PrefetchActivity, NoteBoardView.Presenter,
		LoggedUserChangedEvent.Handler, RefreshNowEvent.Handler {

	private static FormatterLogger logger = FormatterLogger.getLogger(NoteBoardActivity.class);

	private NoteRequestFactory poZit4GrupitDataRequestFactory = GWT
			.<NoteRequestFactory> create(NoteRequestFactory.class);
	private RequestQueue noteRequests = new RequestQueue(false);

	private GrupitExceptionController grupitExceptionController;
	private NoteBoardView display;

	protected UserVo.Id userEntityId;

	private UserVo loggedUserProxy;

	private PoZitData markedToDelete;

	private GlobalProvider grupitGlobalProvider;

	private GrupitMessageBoxFactory grupitMessageBoxFactory;

	private GrupitRequestTransport grupitRequestTransport;

	public NoteBoardActivity(GrupitClientFactory grupitClientFactory) {
		this.grupitExceptionController = grupitClientFactory.getExceptionController();
		this.grupitGlobalProvider = grupitClientFactory.getGlobalProvider();
		this.grupitMessageBoxFactory = grupitClientFactory.getMessageBoxFactory();
		this.grupitRequestTransport = grupitClientFactory.getGrupitRequestTransport();

		this.display = new NoteBoardViewImpl();
		this.display.setPresenter(this);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.google.gwt.activity.shared.Activity#start(com.google.gwt.user.client
	 * .ui.AcceptsOneWidget, com.google.gwt.event.shared.EventBus)
	 */
	@Override
	public void start(AcceptsOneWidget panel, EventBus eventBus) {
		logger.info("Iniciando tablon de notas...");

		RefreshNowEvent.register(eventBus, this);

		this.grupitGlobalProvider.getLoggedUser(new GlobalReceiver<UserVo>() {

			@Override
			public void getGlobalValue(UserVo value) {
				setLoggedUserProxy(value);
			}
		});

		/**
		 * Mostramos la vista
		 */
		panel.setWidget(this.display);

		logger.info("... tablon de notas iniciado.");
	}

	@Override
	public void createPoZit(int x, int y, String targetSystemId) {
		if (targetSystemId == null) {
			targetSystemId = (this.loggedUserProxy != null) ? this.loggedUserProxy.getSystemId() : null;
			assert (targetSystemId != null) : "No se envió el usuario logado. Debe haber un usuario logado para poder crear una nota para este.";
		}

		assert (targetSystemId != null) : "No se puede crear una nota. No se pasó por parámetro un usuario correcto para crearle la nota.";

		logger.info("Creando poZit en el tablón del usuario {0} en la posición ({1},{2})", targetSystemId, x, y);

		NoteRequest context = poZit4GrupitDataRequestFactory.poZit4GrupitRequest();

		NoteProxy newGrupitNote = context.create(NoteProxy.class);
		newGrupitNote.setOwnerUserId((this.loggedUserProxy != null) ? this.loggedUserProxy.getSystemId() : null);
		newGrupitNote.setTargetSystemId(targetSystemId);
		newGrupitNote.setX(x);
		newGrupitNote.setY(y);

		PoZitBoardPanel panel4Target = this.display.noteBoardPanel(targetSystemId);
		PoZitData newGrupitNoteData = new PoZitData();
		newGrupitNoteData.setId(newGrupitNote.stableId());
		newGrupitNoteData.setX(x);
		newGrupitNoteData.setY(y);
		panel4Target.notes().add(newGrupitNoteData);

		Request<Void> request = context.save().using(newGrupitNote);
		noteRequests.fireOrQueue(request, new GrupitReceiverAdapter<Void>(grupitExceptionController));
	}

	@Override
	public void changeNoteCoord(EntityProxyId<? extends PoZitProxy> noteId, final int x, final int y) {
		logger.info("Cambiando coordenadas del poZit {0} a ({1},{2})", noteId, x, y);

		Request<? extends PoZitProxy> context = poZit4GrupitDataRequestFactory.poZit4GrupitRequest().find(noteId);
		noteRequests.fireOrQueue(context, new GrupitReceiverAdapter<PoZitProxy>(grupitExceptionController) {
			public void success(PoZitProxy response) {
				NoteRequest context = poZit4GrupitDataRequestFactory.poZit4GrupitRequest();
				NoteProxy note = (NoteProxy) response;
				NoteProxy editableNote = context.edit(note);
				editableNote.setX(x);
				editableNote.setY(y);

				save(editableNote, context);
			};
		});

	}

	@Override
	public void changeNoteText(EntityProxyId<? extends PoZitProxy> noteId, final String text) {
		logger.info("Cambiando texto del poZit {0} a '{1}')", noteId, text);

		Request<? extends PoZitProxy> context = poZit4GrupitDataRequestFactory.poZit4GrupitRequest().find(noteId);
		noteRequests.fireOrQueue(context, new GrupitReceiverAdapter<PoZitProxy>(grupitExceptionController) {
			public void success(PoZitProxy response) {
				NoteRequest context = poZit4GrupitDataRequestFactory.poZit4GrupitRequest();
				NoteProxy note = (NoteProxy) response;
				NoteProxy editableNote = context.edit(note);
				editableNote.setNoteText(text);

				save(editableNote, context);
			};
		});
	}

	protected void save(NoteProxy note, final NoteRequest context) {
		String noteText = note.getNoteText();
		Integer x = note.getX();
		Integer y = note.getY();
		logger.info("Guardando nota: '{0}' ({1},{2})", noteText, x, y);

		Request<Void> request = context.save().using(note);

		noteRequests.fireOrQueue(request, new GrupitReceiverAdapter<Void>(grupitExceptionController) {

			@Override
			public void success(Void response) {
				logger.info("Nota guardada ok");
			}
		});

	}

	@Override
	public void onLoggedUserChanged(LoggedUserChangedEvent event) {
		this.grupitGlobalProvider.getLoggedUser(new GlobalReceiver<UserVo>() {

			@Override
			public void getGlobalValue(UserVo newLoggedUserProxy) {
				setLoggedUserProxy(newLoggedUserProxy);
			}
		});
	}

	protected void setLoggedUserProxy(UserVo newLoggedUserProxy) {
		if (newLoggedUserProxy != null
				&& (this.loggedUserProxy == null || !this.loggedUserProxy.getId().equals(
						newLoggedUserProxy.getId()))) {
			this.display.setLoggedUserPanelTargetSystemId(newLoggedUserProxy.getSystemId());
			loadNoteBoard(newLoggedUserProxy);
		} else {
			//TODO: Borrar el note board si ya no hay usuario logado.
		}

		this.loggedUserProxy = newLoggedUserProxy;
	}

	protected void loadNoteBoard(UserVo user) {
		final String targetSystemId = user.getSystemId();
		PoZitBoardPanel panel4Target = this.display.noteBoardPanel(targetSystemId);
		panel4Target.setLoading(true);
		Request<List<NoteProxy>> context = poZit4GrupitDataRequestFactory.poZit4GrupitRequest()
				.findAllNotesByUserId(user.getSystemId());
		noteRequests.fireOrQueue(context, new GrupitReceiverAdapter<List<NoteProxy>>(grupitExceptionController) {
			@Override
			public void success(List<NoteProxy> response) {
				refreshNoteBoardPanel(targetSystemId, response);
			}

			@Override
			public void onEnd(boolean success) {
				PoZitBoardPanel panel4Target = display.noteBoardPanel(targetSystemId);
				panel4Target.setLoading(false);
			}
		});
	}

	protected void refreshNoteBoardPanel(String targetSystemId, List<NoteProxy> notes) {
		PoZitBoardPanel panel4Target = this.display.noteBoardPanel(targetSystemId);
		panel4Target.notes().clear();
		for (NoteProxy note : notes) {
			if (this.markedToDelete == null || !this.markedToDelete.getId().equals(note.stableId())) {
				PoZitData pzd = new PoZitData();
				pzd.setId(note.stableId());
				pzd.setX(note.getX());
				pzd.setY(note.getY());
				pzd.setNoteText(note.getNoteText());
				panel4Target.notes().add(pzd);
			} else {
				deleteMarkedToDelete();
			}
		}
		panel4Target.setLoading(false);
	}

	@Override
	public void onRefresh(RefreshNowEvent event) {
		logger.entering("onRefresh");
		logger.info("Evento de refresco recibido. Recargando tablón de notas...");
		if (this.loggedUserProxy != null) {
			loadNoteBoard(this.loggedUserProxy);
		}
		logger.exiting("onRefresh");
	}

	@Override
	public void prefetch(com.google.web.bindery.event.shared.EventBus eventBus) {
		logger.entering("prefetch");
		
		poZit4GrupitDataRequestFactory.initialize(eventBus, this.grupitRequestTransport);
		LoggedUserChangedEvent.register(eventBus, this);

		logger.exiting("prefetch");
	}

	@Override
	public void sendToBin(final PoZitData toSend, final String targetSystemId, final ConfirmResponse confirmResponse) {
		logger.entering("delete", "Marcando para eliminar la nota {0}", toSend);
		this.grupitMessageBoxFactory.confirm("Papelera", "¿Eliminamos esta nota?",
				new GrupitMessageBoxFactory.ConfirmResponse() {

					@Override
					public void onUserResponse(boolean userResponse) {
						confirmResponse.onUserResponse(userResponse);
						if (userResponse) {
							PoZitBoardPanel panel4Target = display.noteBoardPanel(targetSystemId);
							if (markedToDelete != null) {
								logger.info(
										"Ya había una nota marcada para eliminar anteriormente. Se elimina de verdad la nota {0}",
										markedToDelete);
								PoZitData toDelete = markedToDelete;
								delete(toDelete);
							}

							markedToDelete = toSend;
							panel4Target.setUndoActive(true);
							panel4Target.notes().remove(toSend);
						}
					}
				});

		logger.exiting("delete");
	}

	@Override
	public void delete(PoZitData toDelete) {
		EntityProxyId<? extends PoZitProxy> noteId = toDelete.getId();
		Request<? extends PoZitProxy> context = poZit4GrupitDataRequestFactory.poZit4GrupitRequest().find(noteId);
		noteRequests.fireOrQueue(context, new GrupitReceiverAdapter<PoZitProxy>(grupitExceptionController) {
			public void success(PoZitProxy response) {
				NoteRequest context = poZit4GrupitDataRequestFactory.poZit4GrupitRequest();
				NoteProxy note = (NoteProxy) response;
				Request<Void> request = context.delete().using(note);

				noteRequests.fireOrQueue(request, new GrupitReceiverAdapter<Void>(grupitExceptionController) {

					@Override
					public void success(Void response) {
						logger.info("Nota eliminada ok");
					}
				});
			}
		});
	}

	@Override
	public void undoSentToBin(String targetSystemId) {
		PoZitBoardPanel panel4Target = this.display.noteBoardPanel(targetSystemId);
		if (this.markedToDelete != null) {
			panel4Target.notes().add(this.markedToDelete);
		}
		panel4Target.setUndoActive(false);

		this.markedToDelete = null;
	}

	@Override
	public void onStop() {
		logger.entering("onStop");
		super.onStop();

		deleteMarkedToDelete();

		logger.exiting("onStop");
	}

	protected void deleteMarkedToDelete() {
		if (this.markedToDelete != null) {
			logger.info("Eliminando forzosamente la nota marcada para eliminar...");
			EntityProxyId<? extends PoZitProxy> noteId = this.markedToDelete.getId();
			Request<? extends PoZitProxy> context = poZit4GrupitDataRequestFactory.poZit4GrupitRequest().find(noteId);
			final PoZitData deletingNote = markedToDelete;
			noteRequests.fireOrQueue(context, new GrupitReceiverAdapter<PoZitProxy>(grupitExceptionController) {
				public void success(PoZitProxy response) {
					/** Pudo ya haber sido eliminada */
					if (response != null) {
						NoteRequest context = poZit4GrupitDataRequestFactory.poZit4GrupitRequest();
						NoteProxy note = (NoteProxy) response;
						Request<Void> request = context.delete().using(note);

						PoZitBoardPanel panel4Target = display.noteBoardPanel(note.getTargetSystemId());
						panel4Target.setUndoActive(false);
						panel4Target.notes().remove(deletingNote);

						noteRequests.fireOrQueue(request, new GrupitReceiverAdapter<Void>(grupitExceptionController) {

							@Override
							public void success(Void response) {
								logger.info("Nota eliminada ok");
								if (deletingNote.equals(markedToDelete)) {
									markedToDelete = null;
								}
							}
						});
					}
				}
			});
		}
	}

	@Override
	public void clearBin(String targetSystemId) {
		PoZitBoardPanel panel4Target = this.display.noteBoardPanel(targetSystemId);
		if (this.markedToDelete != null) {
			delete(markedToDelete);
			panel4Target.setUndoActive(false);
			panel4Target.notes().remove(markedToDelete);
			this.markedToDelete = null;
		}
	}
}
